﻿using Core.Framework.Distributed.Model;
using Core.Framework.Model.Common;
using Core.Framework.Util;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Core.Framework.Distributed
{

    /// <summary>
    /// 分布式会话保持
    /// 核心
    /// </summary>
    public class CoreDistributed
    {

        #region Parameters

        /// <summary>
        /// 重试连接次数
        /// 每次重试会延长等待时间
        /// 最大间隔时间未25S
        /// </summary>
        static int ReloadConnectCount { get; set; } = 0;

        /// <summary>
        /// 最后心跳时间
        /// </summary>
        static DateTime LastHeartBeat = DateTime.Now;

        /// <summary>
        /// 心跳是否超时
        /// </summary>
        static bool IsHeartBeatTimeOut { get { return LastHeartBeat.AddSeconds(10) <= DateTime.Now; } }

        /// <summary>
        /// 心跳数据
        /// </summary>
        static ArraySegment<byte> _heartBeatMsg;

        /// <summary>
        /// 客户端会话
        /// </summary>
        static ClientWebSocket _socket;

        /// <summary>
        /// 会话Token
        /// </summary>
        static CancellationToken _token;

        /// <summary>
        /// 任务字典
        /// </summary>
        protected static ConcurrentDictionary<string, TaskDicEntity> _TaskDic = new ConcurrentDictionary<string, TaskDicEntity>();

        #endregion


        /// <summary>
        /// 创建实例
        /// 单例
        /// </summary>
        public static void CreateInstance()
        {
            if (_socket == null)
            {
                Task.Run(() => HeartBeat());
            }
        }


        /// <summary>
        /// 服务地址
        /// </summary>
        static Uri _uri()
        {
            var clientInfo = new DistributedClientInfo
            {
                Title = CoreStartupHelper.GetConfigValue("Service:Title"),
                ServicePath = CoreStartupHelper.GetConfigValue("Service:Url")
            }.TryToJson();

            _heartBeatMsg = new ArraySegment<byte>(Encoding.UTF8.GetBytes("h"));

            return new Uri(uriString: $"{CoreStartupHelper.GetConfigValue("Service:Distributed")}/{clientInfo}");
        }

        /// <summary>
        /// 建立连接
        /// </summary>
        static async void Connect()
        {
            _socket = new ClientWebSocket();
            _token = new CancellationToken();

            try
            {
                await _socket.ConnectAsync(uri: _uri(), cancellationToken: _token);
                ReloadConnectCount = 0;
            }
            catch
            {
                ReloadConnectCount = ReloadConnectCount + 1;
            }

            LastHeartBeat = DateTime.Now;
            await RecvAsync();
        }

        /// <summary>
        /// 发送消息
        /// </summary>
        /// <param name="msg"></param>
        protected static bool BeginSend(string msg)
        {
            return Send(new ArraySegment<byte>(Encoding.UTF8.GetBytes(msg)));
        }

        /// <summary>
        /// 发送消息
        /// </summary>
        /// <param name="msg"></param>
        protected static bool BeginSend<T>(T entity)
           where T : class
        {
            return Send(new ArraySegment<byte>(Encoding.UTF8.GetBytes(entity.TryToJson())));
        }

        /// <summary>
        /// 发送消息
        /// </summary>
        /// <param name="h"></param>
        protected static bool Send(ArraySegment<byte> msbBuf)
        {
            try
            {
                if (_socket?.State == WebSocketState.Open)
                {
                    var rrr = _socket.SendAsync(
                    msbBuf,
                    WebSocketMessageType.Text,
                    true,
                    _token);

                    rrr.Wait();

                    return true;

                }
                else
                {
                    return false;
                }
                    
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// 发送心跳
        /// </summary>
        static void SendHeartBeat()
        {
            Send(_heartBeatMsg);
        }

        /// <summary>
        /// 心跳连接
        /// </summary>
        static void HeartBeat()
        {

            while (true && !CoreStartupHelper.IsApplicationStopping)
            {
                Thread.Sleep(1000);

                // 检测心跳超时
                if (IsHeartBeatTimeOut)
                {
                    _socket?.Dispose();
                    _socket = null;
                }

                if (_socket?.State == WebSocketState.Open)
                {
                    SendHeartBeat();
                }
                else
                {
                    if (_socket != null)
                        _socket?.Dispose();

                    

                    if (ReloadConnectCount > 0)
                    {
                        if (ReloadConnectCount > 6)
                            ReloadConnectCount = 5;
                        
                        Thread.Sleep(ReloadConnectCount++ * ReloadConnectCount++ * 1000);
                    }

                    LastHeartBeat = DateTime.Now.AddSeconds(19);

                    // 尝试重连
                    Connect();

                }

            }
        }



        #region 消息处理

        /// <summary>
        /// 接受消息
        /// </summary>
        /// <param name="webSocket">会话session</param>
        /// <param name="cancellationToken">CancellationToken</param>
        /// <param name="OutLogin">退出登陆</param>
        /// <returns></returns>
        static async Task RecvAsync()
        {

            using (MemoryStream memory = new MemoryStream())
            {

                var buffer = new ArraySegment<byte>(new byte[1024]);

                WebSocketReceiveResult result;

                // 接受消息
                do
                {
                    _token.ThrowIfCancellationRequested();

                    result = await _socket.ReceiveAsync(buffer, _token);

                    memory.Write(buffer.Array, buffer.Offset, result.Count - buffer.Offset);

                } while (!result.EndOfMessage);

                memory.Seek(0, SeekOrigin.Begin);

                using (StreamReader reader = new StreamReader(memory))
                {

                    // 继续接受消息
                    if (_socket.State == WebSocketState.Open)
                    {


                        try
                        {

                            var msg = reader.ReadToEndAsync();

                            // 处理接收到的消息
                            var rrr = Task.Run(() => MessageBranchAction(msg.Result));

                            // 循环接收
                            await RecvAsync();

                            reader.Dispose();
                            memory.Dispose();
                        }
                        catch
                        {
                            _socket.Dispose();
                        }

                    }
                    else
                    {
                        _socket.Dispose();
                    }

                }

            }

        }


        /// <summary>
        /// 消息处理
        /// </summary>
        /// <param name="message"></param>
        static void MessageBranchAction(string message)
        {

            if (message.Equals("h"))
            {
                CoreDistributed.LastHeartBeat = DateTime.Now;
                return;
            }

            var jObejct = Newtonsoft.Json.JsonConvert.DeserializeObject<JObject>(message);

            var state = jObejct["State"]?.ToString();
            var LockToken = jObejct["Token"]?.ToString();


            switch (state)
            {

                // 被占用
                case "0":

                    _TaskDic.TryRemove(LockToken, out TaskDicEntity entity0);

                    try
                    {
                        if (entity0 != null)
                        {
                            // 成功任务不存在调用失败方法
                            if (entity0.TaskFail != null)
                            {
                                entity0.TaskFail.Invoke();
                            }
                        }
                    }
                    catch
                    {
                        BeginSend("ReleaseLock:" + LockToken);
                    }

                    break;

                // 成功
                case "1":

                    _TaskDic.TryRemove(LockToken, out TaskDicEntity entity1);

                    try
                    {
                        if (entity1 != null)
                        {
                            // 检测任务是否存在
                            if (entity1.TaskSuccess != null)
                            {
                                entity1.TaskSuccess.Invoke();
                            }
                            else
                            {
                                // 成功任务不存在调用失败方法
                                if (entity1.TaskFail != null)
                                {
                                    entity1.TaskFail.Invoke();
                                }
                            }
                        }
                    }
                    catch
                    {

                    }
                    finally {
                        BeginSend("ReleaseLock:" + LockToken);
                    }

                    break;
            }

        }


        #endregion

    }
}
